【Spring4.0】Spring基于注解方式进行事务管理 您所在的位置:网站首页 cause todo 【Spring4.0】Spring基于注解方式进行事务管理

【Spring4.0】Spring基于注解方式进行事务管理

2024-06-02 03:49| 来源: 网络整理| 查看: 265

一、什么是事务管理 事务管理是企业级应用程序开发中必不可少的技术, 用来确保数据的完整性和一致性.。事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用。

事务的四个关键属性(ACID):

属性解释原子性(atomicity)事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用一致性(consistency)一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中隔离性(isolation)可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏持久性(durability)一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中 Spring 的核心事务管理抽象是PlatformTransactionManager它为事务管理封装了一组独立于技术的方法. 无论使用 Spring 的哪种事务管理策略(编程式或声明式), 事务管理器都是必须的。DataSourceTransactionManager:在应用程序中只需要处理一个数据源, 而且通过 JDBC 存取。JtaTransactionManager: 在 JavaEE 应用服务器上用 JTA(Java Transaction API) 进行事务管理HibernateTransactionManager:用 Hibernate 框架存取数据库事务管理器以普通的 Bean 形式声明在 Spring IOC 容器中

二、为什么要使用事务管理

举个例子:     在一个卖书的商城系统中,肯定会有一个数据库表记录书本价格、书本的库存、用户的余额。当一个用户想买一本价值100元的书,在他结账的时候用户余额会减去100元,而书的库存就会减去1本,这就是一个事务单元。当用户的余额只剩下90元,不足以购买100元的书,这个时候肯定会抛出一个余额不足的异常,虽然账户余额由于异常没有再减钱,但是减去书本库存并没有出现异常,所以依旧会减去1本书的库存,这样做的话就不符合常规,常规应该时在扣钱的时候库存才减去。为了避免这种情况,事务管理就诞生了,它要求一个事务单元要么全都完成,只要当中有一个流程失败,整个事务单元就会回滚到原来没改动前的状态。

三、使用注解方式进行事务管理流程

一般在xml配置文件中启用事务注解即可 这里写图片描述

四、实例的传播属性

事务传播属性可以在 @Transactional 注解的 propagation 属性中定义

参考资料:孟凡柱的专栏

传播属性解释PROPAGATION_REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常。PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

参考资料:尚硅谷 这里写图片描述

REQUIRED和REQUIRES_NEW区别

REQUIRED 传播行为:当 bookService 的 purchase() 方法被另一个事务方法 checkout() 调用时, 它默认会在现有的事务内运行. 这个默认的传播行为就是 REQUIRED. 因此在 checkout() 方法的开始和终止边界内只有一个事务. 这个事务只在 checkout() 方法结束的时候被提交, 结果用户一本书都买不了 REQUIRES_NEW传播行为:另一种常见的传播行为是 REQUIRES_NEW. 它表示该方法必须启动一个新事务, 并在自己的事务内运行. 如果有事务在运行, 就应该先挂起它.

五、示例代码

注意!!

在进行本实验之前请做好JdbcTemplate的配置,如果没有了解过JdbcTemplate,请翻阅我的【Spring4.0】系列日志,里面有提到JdbcTemplate相关内容。

(1)了解需求

需要创建的类: 这里写图片描述 数据库的表: 这里写图片描述 数据库名叫spring 各个表的详情: 这里写图片描述 (account表) 这里写图片描述 (book表) 这里写图片描述 (book_stock表)

(2)数据库创建相关的表 CREATE DATABASE IF NOT EXISTS `spring` /*!40100 DEFAULT CHARACTER SET utf8 */; USE `spring`; -- MySQL dump 10.13 Distrib 5.7.9, for Win64 (x86_64) -- -- Host: localhost Database: spring -- ------------------------------------------------------ -- Server version 5.7.10-log /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; /*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- -- Table structure for table `account` -- DROP TABLE IF EXISTS `account`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `account` ( `username` varchar(50) NOT NULL, `balance` int(11) DEFAULT NULL, PRIMARY KEY (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `account` -- LOCK TABLES `account` WRITE; /*!40000 ALTER TABLE `account` DISABLE KEYS */; INSERT INTO `account` VALUES ('AA',200); /*!40000 ALTER TABLE `account` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `book` -- DROP TABLE IF EXISTS `book`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `book` ( `isbn` varchar(50) NOT NULL, `book_name` varchar(100) DEFAULT NULL, `price` int(11) DEFAULT NULL, PRIMARY KEY (`isbn`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `book` -- LOCK TABLES `book` WRITE; /*!40000 ALTER TABLE `book` DISABLE KEYS */; INSERT INTO `book` VALUES ('1001','Java',100),('1002','Oracle',70); /*!40000 ALTER TABLE `book` ENABLE KEYS */; UNLOCK TABLES; -- -- Table structure for table `book_stock` -- DROP TABLE IF EXISTS `book_stock`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `book_stock` ( `isbn` varchar(50) NOT NULL, `stock` int(11) DEFAULT NULL, PRIMARY KEY (`isbn`), CONSTRAINT `book_stock_isbn` FOREIGN KEY (`isbn`) REFERENCES `book` (`isbn`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*!40101 SET character_set_client = @saved_cs_client */; -- -- Dumping data for table `book_stock` -- LOCK TABLES `book_stock` WRITE; /*!40000 ALTER TABLE `book_stock` DISABLE KEYS */; INSERT INTO `book_stock` VALUES ('1001',10),('1002',10); /*!40000 ALTER TABLE `book_stock` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; -- Dump completed on 2018-08-06 21:12:05 (3)导入相关的包

这里写图片描述

(4)创建外部配置文件db.properties

在src根目录下创建db.properties外部配置文件,用于连接MySQL数据库

jdbc.user=root jdbc.password=123456 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc\:mysql\://localhost\:3306/spring jdbc.initPoolSize=5 jdbc.maxPoolSize=10 (5)创建applicationContext-tx-xml.xml配置文件 (6)创建接口BookShopServiceDao和BookShopService

BookShopServiceDao

package com.spring.tx; public interface BookShopDao { //根据书号获取书的单价 public int findBookPriceByIsbn(String isbn); //更新书的库存,使书号对应的库存-1 public void updateBookStock(String isbn); //更新用户账户余额:使username的balance - price public void updateUserAccount(String username,int price); }

BookShopService

package com.spring.tx; public interface BookShopDao { //根据书号获取书的单价 public int findBookPriceByIsbn(String isbn); //更新书的库存,使书号对应的库存-1 public void updateBookStock(String isbn); //更新用户账户余额:使username的balance - price public void updateUserAccount(String username,int price); } (7)创建异常类UserAccountException和BookStockException

两个类继承RuntimeException并且把相对应的构造器重写即可 UserAccountException

package com.spring.tx; public class UserAccountException extends RuntimeException{ /** * */ private static final long serialVersionUID = 3217299177229543901L; public UserAccountException() { super(); // TODO Auto-generated constructor stub } public UserAccountException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } public UserAccountException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public UserAccountException(String message) { super(message); // TODO Auto-generated constructor stub } public UserAccountException(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } }

BookStockException

package com.spring.tx; public class BookStockException extends RuntimeException { /** * */ private static final long serialVersionUID = -8345575141732656052L; public BookStockException() { super(); // TODO Auto-generated constructor stub } public BookStockException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } public BookStockException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public BookStockException(String message) { super(message); // TODO Auto-generated constructor stub } public BookStockException(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } } (8)创建实现类BookShopServiceImpl和BookShopServiceImpl

BookShopServiceImpl

package com.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository("BookShopDao") public class BookShopDaoImpl implements BookShopDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public int findBookPriceByIsbn(String isbn) { String sql = "SELECT price FROM book WHERE isbn = ?"; return jdbcTemplate.queryForObject(sql, Integer.class, isbn); } @Override public void updateBookStock(String isbn) { // 检查书的库存是否足够,若不够则抛出异常 String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?"; int stock = jdbcTemplate.queryForObject(sql2, Integer.class, isbn); if(stock == 0){ throw new BookStockException("书本库存不足"); } String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?"; jdbcTemplate.update(sql, isbn); } @Override public void updateUserAccount(String username, int price) { String sql2 = "SELECT balance FROM account WHERE username = ?"; int balance = jdbcTemplate.queryForObject(sql2, Integer.class, username); if(balance @Autowired private BookShopDao bookShopDao; /* * 添加事务注解(non-Javadoc) *1.使用propagation 指定事务的传播行为,即当前的事务方法被另一个事务方法调用时 *如何使用事务,默认值为REQUIRED,即使用调用方法的事务 *REQUIRES_NEW:使用自己的事务,调用事务方法的事务挂起 *2.使用isolation指定事务隔离级别,最常用的值为READ_COMMITTED *3.默认情况下Spring的声明式事务对所有异常进行回滚,但是也可以通过属性进行设置,正常情况下默认值即可 *noRollbackFor对某些类不进行回滚 *RollbackFor *4.使用readOnly属性指定事务是否为只读,表示这个事务数据但不更新数据,这样可以帮数据库引擎优化事务 *5.使用timeOut指定强制回滚之前事务可以占用的时间,单位是秒 * @see com.spring.tx.BookShopService#purchase(java.lang.String, java.lang.String) */ @Transactional(propagation = Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED) @Override public void purchase(String username, String isbn) { //1.获取书的单价 int price = bookShopDao.findBookPriceByIsbn(isbn); //2.更新数据库库存 bookShopDao.updateBookStock(isbn); //3.更新用户价格 bookShopDao.updateUserAccount(username, price); } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有